//the data is one dimensional array
// let trianglePointData = [0.3, 0, 0.1, 0, 0, 0.3, -0.1, 0, -0.3, 0, -0.2, -0.1, -0.3, -0.3, 0, -0.2, 0.3, -0.3, 0.2, -0.1];
//[0.1, 0, 0.1, 0.4, 0.3, 0.3, 0, 0.6, -0.3, 0.3, -0.1, 0.4, -0.1, 0][0.2, 0, 0.2, 0.4, 0, 0.2, -0.2, 0.4, -0.2, 0]

//rotational===true anticlockwise otherwise is clockwise
const pointsInTriangle = (point1, point2, point3, rotational = true, data) => {
  let [ab, bc, ca] = [
    [point2.x - point1.x, point2.y - point1.y],
    [point3.x - point2.x, point3.y - point2.y],
    [point1.x - point3.x, point1.y - point3.y]
  ];
  // let pointInTriangleFlag = true;
  for (let i = 0, length = data.length; i < length; i++) {
    let p = data[i];
    if (p !== point1 && p !== point2 && p !== point3 && !p.done) {
      let [ap, bp, cp] = [
        [p.x - point1.x, p.y - point1.y],
        [p.x - point2.x, p.y - point2.y],
        [p.x - point3.x, p.y - point3.y]
      ]

      let z1 = ab[0] * ap[1] - ab[1] * ap[0];
      let z2 = bc[0] * bp[1] - bc[1] * bp[0];
      let z3 = ca[0] * cp[1] - ca[1] * cp[0];
      if (z1 > 0 && z2 > 0 && z3 > 0 || z1 < 0 && z2 < 0 && z3 < 0) {
        return true;
      }
    }
  }
  return false;
}

//orientation===false anticlockwise   return true is ear point ,false maybe concave or some points in the triangle
const judgePoint = (point, data, orientation = false) => {
  let previous = data[point.previous];
  let next = data[point.next];

  let ap = [point.x - previous.x, point.y - previous.y];
  let pb = [next.x - point.x, next.y - point.y];

  if (ap[0] * pb[1] - ap[1] * pb[0] < 0) {
    point.isConcave = true;
    point.isEar = false;
    return false;
  }
  if (!pointsInTriangle(previous, point, next, true, data)) {
    point.isEar = true;
    return true;
  }
  return false;
}

const getPoints = data => {
  let earPoints = [];
  let concavePoints = [];
  for (let i = 0, length = data.length; i < length; i += 1) {
    if (data[i].done) continue;
    let flag = judgePoint(data[i], data);
    if (flag) {
      earPoints.push(data[i]);
    } else {
      if (data[i].isConcave) {
        concavePoints.push(data[i]);
      }
    }
  } 
  return {
    earPoints,
    concavePoints,
  }
}

const isObjInArray = (obj, array) => {
  let arr = array.filter(value => value === obj);
  if (arr.length > 0) return true;
  return false;
}

let gl;

const webglInit = () => {
  const VSHADER_SOURCE = `
            attribute vec4 a_Position;
            attribute float a_PointSize;
            void main(){
            gl_Position = a_Position;
            gl_PointSize =8.0;
            }`;

  const FSHADER_SOURCE = `
            precision mediump float;
            uniform vec4 u_FragColor;
            void main(){
                gl_FragColor = u_FragColor;
            }`;

  initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE);
  let a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(a_Position);
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
}

const generateRenderData = (trianglePointData, offsetIndex = 0) => {
  let formatData = [];
  for (let i = 0, length = trianglePointData.length; i < length; i = i + 2) {
    formatData.push({
      previous: ((i - 2 + length) / 2) % (length / 2),
      next: ((i + 2) / 2) % (length / 2),
      isEar: false,
      index: i / 2,
      isConcave: false,
      x: trianglePointData[i],
      y: trianglePointData[i + 1],
      done: false, //done===true means the points is not in the polygon data.
    })
  }
  let points = getPoints(formatData);
  let triangleIndex = [];
  let earPoints = points.earPoints;
  while (earPoints.length > 0) {
    let point = earPoints.shift();
    if (point.done) continue;
    point.done = true;
    triangleIndex.push(point.previous + offsetIndex, point.index + offsetIndex, point.next + offsetIndex);

    let previous = formatData[point.previous];
    let next = formatData[point.next];

    previous.next = next.index;
    next.previous = previous.index;
    judgePoint(previous, formatData);
    if (previous.isEar) {
      if (!isObjInArray(previous, earPoints)) {
        earPoints.push(previous);
      }
    } else {
      earPoints = earPoints.filter(value => value !== previous);
    }
    judgePoint(next, formatData);
    if (next.isEar) {
      if (!isObjInArray(next, earPoints)) {
        earPoints.push(next);
      }
    } else {
      earPoints = earPoints.filter(value => value !== next);
    }
  }

  return {
    trianglePointData,
    triangleIndex
  }
}

const webglDraw = (data, index) => {
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.DYNAMIC_DRAW);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(index), gl.DYNAMIC_DRAW);
  let u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
  gl.uniform4f(u_FragColor, ...[1.0, 0.0, 0.0, 1.0]);
  gl.drawElements(gl.TRIANGLES, index.length, gl.UNSIGNED_SHORT, 0);
}


export const render = (canvas, data) => {
  gl = getWebGLContext(canvas);
  webglInit();
  let pointData = [];
  let dataIndex = [];
  for (let i = 0, length = data.length; i < length; i++) {
    let obj = generateRenderData(data[i], pointData.length / 2);
    pointData = pointData.concat(obj.trianglePointData);
    dataIndex = dataIndex.concat(obj.triangleIndex);
    if (pointData.length > 5000 || dataIndex.length > 5000) {
      webglDraw(pointData, dataIndex);
      pointData = [];
      dataIndex = [];
    }
  }
  if (pointData.length > 0 && dataIndex.length > 0) {
    webglDraw(pointData, dataIndex);
  }
}